import json
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.forms import model_to_dict
from django.views.decorators.csrf import csrf_exempt
import requests
from utils.tencent.cos import credential
from web.forms.file import FolderModelForm, FileModelForm
from web import models
from utils.tencent.cos import delete_file, delete_file_list
from django.urls import reverse


def file(request,project_id):
    """ GET显示文件列表，POST是添加文件夹"""
    ## 获取文件夹的父类文件夹
    parent_object = None
    folder_id = request.GET.get('folder','') # 通过url后面的？folder=XX来决定folder_id的数据，当folder_id有数字数据时就获取这个对象
    if folder_id.isdecimal():
        # 这个是查找url后面的？folder=XX,其中id=xx的对象
        parent_object = models.Filerepository.objects.filter(id=int(folder_id),
                                                             file_type=2,project=request.tracer.project).first()
    if request.method == 'GET':
        # 循环显示导航条
        breadcrumb_list = []  # 定义导航条列表
        parent = parent_object
        while parent: # 一层层的向上循环，直到parent_id = null
            breadcrumb_list.insert(0,{'id':parent.id,'name':parent.name})
            # breadcrumb_list.insert(0,model_to_dict(parent,['id','name']))
            parent = parent.parent # 每循环一次就让循环的对象等于它的父对象，直到根对象时，根对象的父对象=null 则停止循环

        # 显示文件夹下面的所有文件或者文件夹的列表
        queryset = models.Filerepository.objects.filter(project=request.tracer.project) # 获取当前项目的所有文件
        if parent_object:
            # parent_object有数据时，说明url后面的?folder=XX，是正确的并且可以查找到数据。即说明已经进入了某个目录中
            # 所以可以查找父目录是parent_object的所有对象。上面已经查找出来这个项目的所有文件了，所以直接在这里面查找就可以了
            file_object_list = queryset.filter(parent=parent_object).order_by('-file_type')
        else:
            # parent_object没有数据时，说明是在项目的根目录下面，即这个项目根目录下面的所有文件或者是文件夹
            # 即查找的数据是parent_id为空的所有数据，即项目根目录下面的所有父目录的数据
            file_object_list = queryset.filter(parent__isnull=True).order_by('-file_type')
        # form是让模态框根据表来显示各个字段的输入框，file_object_list是在下面列表中显示的查询出来的所有数据
        form = FolderModelForm(request,parent_object)
        context = {
            "form": form,
            'file_object_list': file_object_list,
            'breadcrumb_list':breadcrumb_list,
            'folder_object':parent_object, # 把获取的当前目录传给前端
        }
        return render(request,'manage/file.html',context)
    # 根据前端传过来的fid是否有数据来判断，执行的是添加操作还是编辑操作。单击模态框提交按钮会把自己建立的存放fid的input标签中的数据也提交过来
    fid = request.POST.get('fid','')
    edit_object = None
    if fid.isdecimal():
        edit_object = models.Filerepository.objects.filter(id=int(fid),
                                                             file_type=2,project=request.tracer.project).first()
    if edit_object:
        form = FolderModelForm(request,parent_object,data=request.POST,instance=edit_object)
    else:
        form = FolderModelForm(request, parent_object, data=request.POST)
    if form.is_valid():
        form.instance.project = request.tracer.project
        form.instance.file_type = 2
        form.instance.update_user = request.tracer.user
        form.instance.parent = parent_object
        form.save()
        return JsonResponse({'status': True})

    return JsonResponse({'status':False,'error':form.errors})

def file_delete(request,project_id):
    """删除文件"""
    fid = request.GET.get('fid')
    delete_object = models.Filerepository.objects.filter(id=fid,project=request.tracer.project).first()
    if delete_object.file_type == 1:
        # 要删除的是文件
        # 删除文件时，将项目中的已使用的空间减去腰删除文件所占空间。即让已删除的空间更新
        request.tracer.project.user_space -= delete_object.file_size
        request.tracer.project.save()
        # 在cos中删除文件
        delete_file(request.tracer.project.bucket,request.tracer.project.region,delete_object.key)
        # 从数据库中删除
        delete_object.delete()
    else:
        # 要删除的是文件夹，需要设置一个列表把要删除的文件夹下面的所有文件和文件夹，以及这些文件夹再下面的文件夹全部循环的找出来
        total_size = 0 # 要删除文件夹下面的所有文件的大小
        folder_list = [delete_object,] # 里面存放要删除的文件夹对象
        # 把需要删除的key放在一个列表中，进行批量删除
        key_list = []
        for folder in folder_list:
            child_list = models.Filerepository.objects.filter(project=request.tracer.project,parent=folder).order_by('-file_type')
            for child in child_list:
                if child.file_type == 2:
                    folder_list.append(child)
                else:
                    total_size += child.file_size
                    # key_list.append(child.key)
                    # 根据批量删除的提示重新构建key保存的列表,让列表中的元素变成一个个的字典
                    key_list.append({'Key':child.key})

        # 调用批量删除文件的程序，进行批量删除
        if key_list:
            delete_file_list(request.tracer.project.bucket,request.tracer.project.region,key_list)
        if total_size:
            request.tracer.project.user_space -= total_size
            request.tracer.project.save()
        delete_object.delete()
    return JsonResponse({'status':True})

@csrf_exempt
def cos_credential(request,project_id):
    """获取临时的凭证"""
    # 接收前端传送过来的JSON格式的数据用request.body,并转换成utf-8格式的
    file_list = json.loads(request.body.decode('utf-8'))
    total_size = 0
    # 获取限制的单个文件的大小
    per_file_limit = request.tracer.price_policy.per_file_size * 1024 * 1024
    total_file_limit = request.tracer.price_policy.project_space * 1024 * 1024
    # 通过循环获取里面的每个文件对象
    for item in file_list:
        # 单文件的字节大小item['size']
        # 判断单文件是否超出限制
        if item['size'] > per_file_limit:
            msg = '文件：{}的大小超出了限制,最大允许的大小是{}M'.format(item['name'],request.tracer.price_policy.per_file_size)
            return JsonResponse({'status':False,'error':msg})
        total_size += item['size']
    # 对总容量进行限制
    # request.tracer.price_policy.project_space # 项目允许的空间
    # request.tracer.project.user_space # 项目已使用的空间
    if request.tracer.project.user_space + total_size > total_file_limit:
        msg = '文件总的大小超出了限制,最大允许的大小是{}M'.format(request.tracer.price_policy.project_space)
        return JsonResponse({'status': False, 'error': msg})
    data_dict = credential(request.tracer.project.bucket,request.tracer.project.region)
    return JsonResponse({'status':True,'data':data_dict})

@csrf_exempt
def file_post(request,project_id):
    """ 上传成功的文件写入到数据库 """

    form = FileModelForm(request,data=request.POST)
    if form.is_valid():
        # 校验数据通过后，将把这些信息写入到数据库中的文件表中。通过form.save()保存的数据，
        # 无法通过instance.get_file_type_display()来获取数据表中，用choice定义的中文例如：文件、文件夹 这样的信息
        # form.instance.file_type = 1 # 文件类型是1，代表的是文件
        # form.update_user = request.tracer.user
        # form.save()
        # 用下面这种方法可以避免上面方法的短处
        data_dict = form.cleaned_data
        data_dict.pop('etag') # 把只是用于校验但是不在数据库中保存的etag从，校验完成的数据中弹出
        data_dict.update({'project':request.tracer.project,'file_type':1,'update_user':request.tracer.user})
        instance = models.Filerepository.objects.create(**data_dict)

        # 项目已使用的空间更新
        request.tracer.project.user_space += data_dict['file_size']
        request.tracer.project.save()

        result = {
            'id': instance.id,
            'name':instance.name,
            'file_size':instance.file_size,
            'file_type':instance.get_file_type_display(), # 不直接用form.save()，用自己构造的字典就可以输出choices对象
            'username': instance.update_user.username,
            'datetime': instance.update_datetime.strftime("%Y年%m月%d日 %H:%M"),
            # 把下载要进入的url，通过反向路由来生成。并传送给前端
            'download_url':reverse('file_download',kwargs={'project_id':project_id,'file_id':instance.id})

        }
        return JsonResponse({'status':True,'data':result})
    return JsonResponse({'status':False,'data':'文件错误'})

# 下载文件，通过传递过来的file_id 来对文件进行下载
def file_download(request,project_id,file_id):
    """ 下载文件 """
    # 文件内容、响应头
    # 打开文件，获取文件内容
    # with open('',mode='rb') as f:
    #     data = f.read()
    ## 通过网络下载文件
    file_object = models.Filerepository.objects.filter(id=file_id,project_id=project_id).first()
    res = requests.get(file_object.file_path)
    # 这是常规方法
    # data = res.content
    # response = HttpResponse(data)
    # response['Content-Disposition'] = 'attachment; filename={}'.format(file_object.name)  # 设置响应头
    # 如果是大文件时，需要分块处理
    data = res.iter_content()
    response = HttpResponse(data,content_type='application/octet-stream') # 用于显示下载提示框
    from django.utils.encoding import escape_uri_path
    response['Content-Disposition'] = 'attachment; filename={}'.format(escape_uri_path(file_object.name)) # 设置响应头，并对文件名进行转义


    return response
















